/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.imagepipeline.xml

import android.content.res.Resources
import android.net.Uri
import androidx.core.content.res.ResourcesCompat
import com.facebook.common.logging.FLog
import com.facebook.common.util.UriUtil
import com.facebook.imagepipeline.common.ImageDecodeOptions
import com.facebook.imagepipeline.decoder.ImageDecoder
import com.facebook.imagepipeline.image.CloseableImage
import com.facebook.imagepipeline.image.DefaultCloseableXml
import com.facebook.imagepipeline.image.EncodedImage
import com.facebook.imagepipeline.image.QualityInfo
import java.util.concurrent.ConcurrentHashMap

internal class XmlFormatDecoder(private val resources: Resources) : ImageDecoder {

  private val resourceIdCache: MutableMap<String, Int> = ConcurrentHashMap()

  override fun decode(
      encodedImage: EncodedImage,
      length: Int,
      qualityInfo: QualityInfo,
      options: ImageDecodeOptions,
  ): CloseableImage? {
    return try {
      val xmlResourceName = encodedImage.source ?: error("No source in encoded image")
      val xmlResourceId = getXmlResourceId(xmlResourceName)
      val drawable = ResourcesCompat.getDrawable(resources, xmlResourceId, null)
      drawable?.let { DefaultCloseableXml(it) }
    } catch (error: Throwable) {
      FLog.e(TAG, "Cannot decode xml", error)
      null
    }
  }

  private fun getXmlResourceId(xmlResourceName: String): Int {
    return resourceIdCache.getOrPut(xmlResourceName) {
      parseImageSourceResourceId(Uri.parse(xmlResourceName))
    }
  }

  /**
   * This parsing implementation is only designed to work with URIs that have been generated by
   * UriUtil, which inserts the resource ID into the path of the URI.
   * - Local resource URI format: res://[resourceId]
   * - Qualified resource URI format: res://[package]/[resourceId]
   *
   * @throws IllegalStateException if the resource ID cannot be parsed from the provided uri
   */
  private fun parseImageSourceResourceId(xmlResource: Uri): Int {
    return if (
        UriUtil.isLocalResourceUri(xmlResource) || UriUtil.isQualifiedResourceUri(xmlResource)
    ) {
      xmlResource.pathSegments.lastOrNull()?.toIntOrNull()
          ?: error("Unable to read resource ID from ${xmlResource.path}")
    } else {
      error("Unsupported uri ${xmlResource}")
    }
  }

  companion object {
    private const val TAG: String = "XmlFormatDecoder"
  }
}
